//============================================================================
// Name        : XMLLogger.cpp
// Author      : Dan Erusalimchik (danerde@gmail.com)
// Version     :
// Copyright   : Cogniteam ltd.
// Description : Hello World in C++, Ansi-style
//============================================================================

#ifndef __LOGGER_XML___H__
#define __LOGGER_XML___H__

#include <iostream>
#include <sstream>
#include <vector>
#include <set>

using namespace std;


#include <boost/thread.hpp>
using namespace boost;


namespace logger{

class Logger;

class object{
public:
	string name;
	stringstream attributes;
	object(string name):name(name){}
	object(const object& o):name(o.name),attributes(""){}
	object& operator=(const object& o){ name=o.name; return *this;}

	void open(Logger& logger)const;
	void close(Logger& logger)const;
	template <class A>
	object& attr(string attn, const A& a){
		attributes<<attn<<"=\""<<a<<"\" ";
		return *this;
	}
};
class tag{
public:
	string name;
	stringstream attributes;
	tag(string name):name(name){}
	void open(Logger& logger)const;
	template <class A>
	tag& attr(string attn, const A& a){
		attributes<<attn<<"=\""<<a<<"\" ";
		return *this;
	}
};

class notype{
public:
	stringstream data;
	void open(Logger& logger)const;
	template <class A>
	notype(const A& a){
		data<<a;
	}
};

class type{
public:
	stringstream data;
	void open(Logger& logger)const;
	template <class A>
	type(const char*& s){
		data<<"<s>"<<s<<"</s>";
	}
	type(const string& s){
		data<<"<s>"<<s<<"</s>";
	}
	type(const double& s){
		data<<"<d>"<<s<<"</d>";
	}
	type(const float& s){
		data<<"<f>"<<s<<"</f>";
	}
	type(const int& s){
		data<<"<i>"<<s<<"</i>";
	}
	type(const char& s){
		data<<"<c>"<<s<<"</c>";
	}
	type(const long& s){
		data<<"<l>"<<s<<"</l>";
	}
	type(const unsigned char& s){
		data<<"<u>"<<s<<"</u>";
	}
};


class Logger{
#define WITH_TYPE notype
public:
	ostream& stream;
	vector<object> objects;
	int tabNum;
	bool onnewline;
	bool isasinc;

	bool logtag_not_avaliable;
	boost::mutex& internal_mutex(){ static boost::mutex _internal_mutex; return _internal_mutex; }
	boost::mutex::scoped_lock* locker;

	typedef string LOGTAG;
	static const LOGTAG LOGTAG_ALL;
	static set<LOGTAG> avaliables_tags;
	#define LOGTAG_AVALIABLE if(logtag_not_avaliable) return *this;
	#define LOGGER_LOCK_GLOBAL_MUTEX locker=new boost::mutex::scoped_lock(internal_mutex());
	#define LOGGER_UNLOCK_GLOBAL_MUTEX if(locker){ delete locker; locker=0; }

	Logger(ostream& c):stream(c), tabNum(0), onnewline(true),isasinc(true),logtag_not_avaliable(false),locker(0){
		LOGGER_LOCK_GLOBAL_MUTEX
		//cout<<"[LOG1] logtag_not_avaliable="<<(logtag_not_avaliable?"true":"false")<<endl;
	}
	Logger(ostream& c, const LOGTAG& lt):stream(c), tabNum(0), onnewline(true),isasinc(true),logtag_not_avaliable(false),locker(0){
		logtag_not_avaliable = avaliables_tags.find(lt)==avaliables_tags.end(); if(logtag_not_avaliable) return;
		LOGGER_LOCK_GLOBAL_MUTEX
		//cout<<"[LOG2] logtag_not_avaliable="<<(logtag_not_avaliable?"true":"false")<<endl;
	}
	Logger(ostream& c, const char* lt):stream(c), tabNum(0), onnewline(true),isasinc(true),logtag_not_avaliable(false),locker(0){
		logtag_not_avaliable = avaliables_tags.find(lt)==avaliables_tags.end(); if(logtag_not_avaliable) return;
		LOGGER_LOCK_GLOBAL_MUTEX
		//cout<<"[LOG2] logtag_not_avaliable="<<(logtag_not_avaliable?"true":"false")<<endl;
	}
	Logger(ostream& c, bool asinc, LOGTAG lt=LOGTAG_ALL):stream(c), tabNum(0), onnewline(true), isasinc(asinc),logtag_not_avaliable(false),locker(0){
		logtag_not_avaliable = avaliables_tags.find(lt)==avaliables_tags.end(); if(logtag_not_avaliable) return;
		if(isasinc){ LOGGER_LOCK_GLOBAL_MUTEX }
		//cout<<"[LOG3] logtag_not_avaliable="<<(logtag_not_avaliable?"true":"false")<<endl;
	}
	~Logger(){
		if(isasinc){ LOGGER_UNLOCK_GLOBAL_MUTEX }
	}

	ostream& tab(){
		if(onnewline==false) newLine();
		for(int i=0;i<tabNum*4;i++)
			stream<<' ';
		onnewline=false;
		return stream;
	}
	ostream& cntn(){
		if(onnewline==true){
			for(int i=0;i<tabNum*4;i++)
				stream<<' ';
		}
		onnewline=false;
		return stream;
	}
	void incTab(){ tabNum+=1; newLine();}
	void decTab(){ tabNum-=1;}
	void newLine(){ stream<<std::endl; onnewline=true;}

	void close(){
		if(objects.size()<1) return;
		objects.back().close(*this);
		objects.erase(objects.end()-1);
	}

	Logger& operator<<(const tag& t){
		LOGTAG_AVALIABLE
		t.open(*this); return *this;
	}
	Logger& operator<<(const object& t){
		LOGTAG_AVALIABLE
		objects.push_back(t);
		t.open(*this); return *this;
	}
	Logger& operator<<(const notype& t){
		LOGTAG_AVALIABLE
		t.open(*this); return *this;
	}
	Logger& operator<<(const type& t){
		LOGTAG_AVALIABLE
		t.open(*this); return *this;
	}
//	Logger& operator<<(const char* s){
//		LOGTAG_AVALIABLE
//		cntn()<<"<s>"<<s<<"</s>"; return *this;
//	}
//	Logger& operator<<(const string& s){
//		LOGTAG_AVALIABLE
//		cntn()<<"<s>"<<s<<"</s>"; return *this;
//	}
//	Logger& operator<<(const double& s){
//		LOGTAG_AVALIABLE
//		cntn()<<"<d>"<<s<<"</d>"; return *this;
//	}
//	Logger& operator<<(const float& s){
//		LOGTAG_AVALIABLE
//		cntn()<<"<f>"<<s<<"</f>"; return *this;
//	}
//	Logger& operator<<(const int& s){
//		LOGTAG_AVALIABLE
//		cntn()<<"<i>"<<s<<"</i>"; return *this;
//	}
//	Logger& operator<<(const char& s){
//		LOGTAG_AVALIABLE
//		cntn()<<"<c>"<<s<<"</c>"; return *this;
//	}
//	Logger& operator<<(const long& s){
//		LOGTAG_AVALIABLE
//		cntn()<<"<l>"<<s<<"</l>"; return *this;
//	}
//	Logger& operator<<(const unsigned char& s){
//		LOGTAG_AVALIABLE
//		cntn()<<"<u>"<<s<<"</u>"; return *this;
//	}
	Logger& operator<<(void(*F)(Logger&)){
		LOGTAG_AVALIABLE
		F(*this); return *this;
	}
	template<class A>
	Logger& operator<<(const A& s){
		return (*this)<<WITH_TYPE(s);
	}

#undef LOGTAG_AVALIABLE
#undef WITH_TYPE
};




}
#endif


